#ifndef GTL_ENGINE_GUI_HEADER_GUARD
#define GTL_ENGINE_GUI_HEADER_GUARD

#include <eastl/vector.h>
#include <GTLEngine/gtlCore.h>
#include <GTL/gtlStructures.h>
#include <GTL/gtlFormats.h>
#include <GTLEngine/gtlResource.h>

namespace GTL {	namespace Engine {

	namespace GUI
	{

		///************************************************************************/
		/// GUI Events
		///************************************************************************/

		namespace Event
		{
			enum
			{
				GUIEvent = 'GUIE',

				MouseDown,
				MouseUp,
				MouseOver,
				MouseOut,
				Click,

				KeyUp,
				KeyDown,
				KeyPressed,
			};
		}

		///************************************************************************/
		/// Immediate Mode GUI
		///************************************************************************/

		namespace IM
		{
			void Start();
			void End();
			int Button(intptr_t ID, float x, float y, const ImageRef* img, const char* text = 0, const FontRef* font = 0, 
				const ColorRGBA& textcolor= ColorRGBA(0,0,0,1));
			int DoLabel(intptr_t ID, float x, float y, const char* text, size_t width = 0, size_t height = 0);

			#define IM_ID (intptr_t)(__FILE__ GTL_STRINGIFY(__LINE__))
		}

		///************************************************************************/
		/// Control
		///************************************************************************/

		/// TODO:
		/// - get the default control classes from somewhere
		/// - maybe set the default font/fontcolor for types? idunno
		/// - autosize label

		class Control
		{
		public:
			/// Typedefs

			typedef GTL::Structures::DataNode::DictType ClassMap;

      /// Parameters
			float X, Y;
			float Width, Height;
			//float ScaleX, ScaleY;
			bool Visible, Enabled, MouseChildren;
			ColorRGBA Color;

			Rect InputMaskRect;
			bool InputMask;

			unsigned long long int Userdata[4];

			Image TheImage;
			Shader TheShader;

      eastl::string Name;

			/// Used primarily by radiobuttons
			eastl::string Group;

      /// Set
      Control& Set(float x, float y, const Image& image, bool size_from_image = true);

      /// Type
			bool Is(uint32_t type) { return type == Type(); }
			virtual uint32_t Type() const { return 'CONT'; }

      /// Visibility
			virtual void Show() { Visible = true; Enabled = true; }
			virtual void Hide() { Visible = false; Enabled = false; }

			/// Parents and children
			Control* GetParent() const { return mParent; }

			/// Children
			Control*	GetChildAt(size_t i) const { return mChildren[i]; }
			int				GetChildIndex(const Control* obj) const;
			Control*	GetControlAtPoint(float x, float y, bool onlyEnabled = false);
      Control*	GetControlWithName(const char* name);
			Control*	GetControlByPath(const char* path);
			int				AddChild(Control* obj);
			int				AddChild(Control& obj);
			Control*	RemoveChildAt(size_t at);
			Control*	RemoveChild(Control* obj);
			template <typename Predicate>
			void			ForEachChild(Predicate predicate)
			{
				eastl::for_each(mChildren.begin(), mChildren.end(), predicate);
			}
			size_t		GetNumChildren() const { return mChildren.size(); }

			int				AddToParent(Control& obj);
			int				AddToParent(Control* obj);

			/// Position and size
			bool	IsPointInside(float x, float y, bool applyInputMask = false) const;
			Point GetPosition() const;
			Rect	GetRect() const;

			/// Events
			bool MouseMove(float x, float y);
			bool MouseDown(float x, float y);
			bool MouseUp(float x, float y);
			bool KeyUp(int key);
			bool KeyDown(int key);
			bool CharEntered(int key, int ch);
			void AddEvent(const GameEvent& event);
			void SetEventBuffer(EventBuffer* evs) { mEvents = evs; }

			/// Rest
			uint32_t Update(float dt);
			uint32_t Draw(float x = 0, float y = 0, const ColorRGBA& col = ColorRGBA(1,1,1,1));

			/// Creating

      static Control* CreateControlFromNode(GTL::Structures::DataNode* node, ClassMap* classes = 0);
      static Control* CreateControlFromWilsonFile(const char* filename, ClassMap* classes = 0);
			static Control* CreateControlFromFile(const char* filename, int fileformat = GTL::Formats::AutoDetect::FormatID, ClassMap* classes = 0);

			Control();
			virtual ~Control();
		protected:
			//Control();
      virtual void BuildFrom(GTL::Structures::DataNode* node);

			void Attached(Control* parent);
			void Unattached();

			virtual void UpdateAt(float dt);
			virtual void DrawAt(float x, float y, const ColorRGBA& color);
			void DestroyChildren();

			uint32_t mFlags;
			bool mIsDynamic;
			Control* mParent;
			typedef eastl::vector<Control*> ChildrenContainer;
			ChildrenContainer mChildren;

			/// Events
			virtual bool HandleEvent(const GameEvent& event);

			virtual bool OnMouseOver(float x, float y);
			virtual bool OnMouseOut(float x, float y);
			virtual bool OnMouseDown(float x, float y);
			virtual bool OnMouseUp(float x, float y);
			virtual bool OnKeyDown(int key);
			virtual bool OnKeyUp(int key);
			virtual bool OnChar(int key, int ch);
			virtual bool OnClick();
			bool mOver;
			bool mPressed;
			EventBuffer* mEvents;

			friend class GUILoader;
		};

		///************************************************************************/
		/// ControlResource
		///************************************************************************/

#define gui(name, from) resource (GUI::Control, name, from)

		class ControlResource : public Resource
		{
			Control* ctrl;
		public:
			virtual int GetType() { return 'ctrl'; }

			virtual void LoadFrom(const char* src);

			virtual void Unload()
			{
			}

			operator Control*() { return ctrl; }
			Control* operator->() { return ctrl; }
		};

		///************************************************************************/
		/// Button
		///************************************************************************/

		class Button : public Control
		{
		public:
			struct ButtonType { enum
			{
				Normal,
				CheckButton,
				RadioButton,
			};};

			struct ButtonState { enum
			{
				Normal,
				MouseOver,
				Pressed,
				Selected,
				Disabled,

				TotalStates
			};};

			ColorRGBA TextColor;

			Button();
			virtual ~Button() {}

			virtual uint32_t Type() const { return 'BUTT'; }

			void SetType(int type);
			void SetFont(const Font& font);
			void SetLabel(const char* label);

			int GetType() const { return mType; }
			Font GetFont() const { return mFont; }
			const char* GetLabel() const { return mLabel; }

			void SetActive(bool active) { mActive = active; }
			bool IsActive() const { return mActive; }

			virtual int GetImageState();

			Button& Set(float x, float y, const Image& image, const Font& font, const char* label, int type = ButtonType::Normal);

		protected:
			virtual void BuildFrom(GTL::Structures::DataNode* node);
			virtual void DrawAt(float x, float y, const ColorRGBA& color);
			//virtual bool OnMouseOver(float x, float y);
			//virtual bool OnMouseOut(float x, float y);
			//virtual bool OnMouseDown(float x, float y);
			//virtual bool OnMouseUp(float x, float y);
			virtual bool OnClick();
			int mType;
			const char* mLabel;
      eastl::string mInternalLabel;
			float mLabelLen;
			Font mFont;

			bool mActive;

      friend class Control;
		};

		///************************************************************************/
		/// Label
		///************************************************************************/

    class Label : public Control
    {
    public:
      ColorRGBA TextColor;

      Label();
      virtual ~Label() {}

      virtual uint32_t Type() const { return 'LABE'; }

      void SetFont(const Font& font);
      void SetLabel(const char* label);

			Font GetFont() const { return mFont; }
			const char* GetLabel() const { return mLabel; }

      Label& Set(float x, float y, const Font& font, const char* label);
    protected:
      virtual void BuildFrom(GTL::Structures::DataNode* node);
      virtual void DrawAt(float x, float y, const ColorRGBA& color);
      const char* mLabel;
      eastl::string mInternalLabel;
      float mLabelLen;
      Font mFont;

			void RecalculateSize();

      friend class Control;
    };

		///************************************************************************/
		/// TextBox
		///************************************************************************/

		class TextBox : public Control
		{
		public:
			ColorRGBA TextColor;

			struct Align { enum
			{
				Left,
				Top = Left,

				Center,

				Right,
				Bottom = Right,
			};};

			int HorizontalAlign;
			int VerticalAlign;

			bool ScrollOnOverflow;

			TextBox();
			virtual ~TextBox() {}

			virtual uint32_t Type() const { return 'TXTB'; }

			void SetFont(const Font& font);
			void SetLabel(const char* label);

			Font GetFont() const { return mFont; }
			const char* GetLabel() const { return mLabel; }

			TextBox& Set(Rect rect, const Font& font, const char* label);
		protected:
			virtual void BuildFrom(GTL::Structures::DataNode* node);
			virtual void DrawAt(float x, float y, const ColorRGBA& color);
			const char* mLabel;
			eastl::string mInternalLabel;
			Font mFont;

			struct Line
			{
				const char* Start;
				int BLen, CPLen;
				float PXLen;
				bool IsWrapped;
				Line() { Reset(); }
				void Reset() { Start = 0; BLen = CPLen = 0; PXLen = 0; IsWrapped = 0; }
			};
			eastl::vector<Line> mLines;

			void Recalculate();

			friend class Control;
		};

		///************************************************************************/
		/// EditBox
		///************************************************************************/

		class EditBox : public Control
		{
		public:
			ColorRGBA TextColor;
			ColorRGBA CursorColor;

			Rect InputArea;

			size_t MaxSize;

			EditBox();
			virtual ~EditBox() {}

			virtual uint32_t Type() const { return 'EDIT'; }

			void SetFont(const Font& font);
			void SetText(const char* text);

			Font GetFont() const { return mFont; }
			eastl::string GetText() const { return mText; }

			EditBox& Set(float x, float y, const Image& image, float width, const Font& font, const char* text = "");
		protected:
			virtual void BuildFrom(GTL::Structures::DataNode* node);
			virtual void DrawAt(float x, float y, const ColorRGBA& color);
			eastl::string mText;
			size_t mTextLength;
			size_t mCursorPos;
			int mBlink;
			float mLabelLen;
			Font mFont;

			virtual void UpdateAt(float dt);
			virtual bool OnChar(int key, int ch);
			virtual bool OnMouseDown(float x, float y);

			void RecalculateSize();

			friend class Control;
		};
	}


}}


#endif